using System.Data;
using System.Text.Json;
using Dapper;
using GEM.Domain.Entities;
using GEM.Domain.Repositories;
using GEM.Infrastructure.Redis;

namespace GEM.Infrastructure.Repositories;

public class DapperRepository<T> : IDapperRepository<T> where T : EntityBase
{

    private readonly IDbConnection _connection;
    private readonly IRedisService _redis;
    public DapperRepository(IDbConnection connection, IRedisService redis)
    {
        _connection = connection;
        _redis = redis;
    }

    public async Task<int> CreateAsync(T entity)
    {
        var type = typeof(T);
        var tableName = type.Name;

        // 过滤掉导航属性，只保留简单类型和 string
        var props = type.GetProperties()
                .Where(p => p.CanRead && p.CanWrite && !typeof(System.Collections.IEnumerable).IsAssignableFrom(p.PropertyType) || p.PropertyType == typeof(string))
                .ToList();

        // 构建字段和参数
        var columns = string.Join(", ", props.Select(p => $"\"{p.Name}\""));
        var values = string.Join(", ", props.Select(p => $"@{p.Name}"));
        // System.Console.WriteLine("columns:  " + columns);
        // System.Console.WriteLine("values:  " + values);
        var sql = $"INSERT INTO \"{tableName}\" ({columns}) VALUES ({values}) RETURNING *;";

        // 执行后返回受影响的行数
        var number = await _connection.ExecuteAsync(sql, entity);
        return number;
    }

    // 真删除
    public async Task<int> DeleteAsync(Guid id)
    {
        var tableName = typeof(T).Name;
        var sql = $"delete from \"{tableName}\" where \"Id\" = @Id";
        var number = await _connection.ExecuteAsync(sql, new { Id = id });
        return number;   //成功返回1，失败返回0
    }

    // 软删除
    public async Task<int> IsDeletedAsync(Guid id)
    {
        var obj = await GetByIdAsync(id);
        if (obj != null)
        {
            obj.IsDeleted = true;
            await UpdateAsync(obj);
            return 1;
        }
        return 0;
    }


    public async Task<IEnumerable<T>> GetAllAsync()
    {
        //  获取类型信息
        var type = typeof(T);
        // 获取表名
        var tableName = type.Name;
        var sql = $"SELECT * FROM \"{tableName}\"";
        // redis缓存中的键
        string cacheKey = $"{tableName}:list";
        //1. 先查 Redis
        var hashEntries = await _redis.HashGetAllObjectsAsync<T>(cacheKey);
        if (hashEntries.Count > 0)
        {
            return hashEntries;
        }

        var list = await _connection.QueryAsync<T>(sql);
        // await WriteListToRedisAsync(list, cacheKey, TimeSpan.FromMinutes(10));

        return list;
    }

    public async Task<T?> GetByIdAsync(Guid id)
    {
        var type = typeof(T);
        var tableName = type.Name;
        var sql = $"select * from \"{tableName}\" where \"Id\" = @Id";
        var obj = await _connection.QueryFirstOrDefaultAsync<T>(sql, new { Id = id });
        return obj;
    }

    public async Task<int> UpdateAsync(T entity)
    {
        // 获取类型信息
        var type = typeof(T);
        // 获取表名
        var tableName = type.Name;
        var props = type.GetProperties()
                .Where(p => p.CanRead && p.CanWrite && p.Name != "Id" && (p.PropertyType.IsValueType || p.PropertyType == typeof(string)))
                .ToList();
        var setClause = string.Join(", ", props.Select(p => $"\"{p.Name}\" = @{p.Name}"));
        var sql = $"UPDATE \"{tableName}\" SET {setClause} WHERE \"Id\" = @Id";
        var number = await _connection.ExecuteAsync(sql, entity);

        // 1. redis缓存中的键
        string cacheKey = $"{tableName}:list";
        // 2. 删除旧缓存
        await _redis.DeleteKeyAsync(cacheKey);
        var sqlList = $"SELECT * FROM \"{tableName}\"";
        var list = await _connection.QueryAsync<T>(sqlList);
        // 3. 更新成功则写入redis并设置过期时间
        if (number > 0)
        {
            // await WriteListToRedisAsync(list, cacheKey, TimeSpan.FromMinutes(10));
        }
        return number;
    }


    //将数据写入redis并且是哈希类型
    private async Task WriteListToRedisAsync(IEnumerable<T> list, string cacheKey, TimeSpan? expire = null)
    {
        await _redis.DeleteKeyAsync(cacheKey);
        foreach (var obj in list)
        {
            await _redis.HashSetObjectAsync(cacheKey, obj.Id.ToString(), obj);
        }
        await _redis.ExpireKeyAsync(cacheKey, expire ?? TimeSpan.FromMinutes(10));
        System.Console.WriteLine($"将{cacheKey}信息写入redis并设置过期时间{expire ?? TimeSpan.FromMinutes(10)}");
    }

}